mod serde_default {
    pub fn assets_is_directory() -> bool {
        false
    }
    pub fn assets_is_foreign() -> bool {
        false
    }
}
use std::path::Path;

use crate::GLOBAL_CONFIG;
use path_slash::PathBufExt;
use serde::{Deserialize, Serialize};
use wither::{
    bson::{doc, oid::ObjectId},
    Model,
};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Assets {
    /// 资源ID
    #[serde(default = "Default::default")]
    pub id: ObjectId,
    pub path: String,
    #[serde(default = "serde_default::assets_is_directory")]
    pub is_directory: bool,
    #[serde(default = "serde_default::assets_is_foreign")]
    pub is_foreign: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssetsDTO {
    pub id: String,
    pub path: String,
    #[serde(default = "serde_default::assets_is_directory")]
    pub is_directory: bool,
    #[serde(default = "serde_default::assets_is_foreign")]
    pub is_foreign: bool,
}

impl From<Assets> for AssetsDTO {
    fn from(a: Assets) -> Self {
        AssetsDTO {
            id: a.id.to_string(),
            path: a.path,
            is_directory: a.is_directory,
            is_foreign: a.is_foreign,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, Model)]
pub struct AssetsModel {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    pub id: Option<ObjectId>,
    pub path: String,
    #[serde(default = "serde_default::assets_is_directory")]
    pub is_directory: bool,
    #[serde(default = "serde_default::assets_is_foreign")]
    pub is_foreign: bool,
    pub mime: Option<String>,
    pub filename: Option<String>,
    pub filename_ext: Option<String>,
    pub owner_id: Option<ObjectId>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssetsModelDTO {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<String>,
    pub path: String,
    pub is_directory: bool,
    pub is_foreign: bool,
    pub mime: Option<String>,
    pub filename: Option<String>,
    pub filename_ext: Option<String>,
}

fn relative_assets_path_to_web_path(assets_path: &str) -> String {
    let (web_base_path, app_base_path) = {
        let config = GLOBAL_CONFIG.read().unwrap();
        config
            .as_ref()
            .map(|v| (v.upload.web_base_path.clone(), v.app.base_path.clone()))
            .unwrap_or(("".into(), "".into()))
    };

    // 使用url解析，如果直接上path的话可能会把`http://` 中的两个斜杠合并
    url::Url::parse(app_base_path.as_str())
        .ok()
        .map(|mut v| {
            v.set_path(
                Path::new(web_base_path.as_str())
                    .join(
                        Path::new(assets_path)
                            .strip_prefix("/")
                            .unwrap_or_else(|_| Path::new(assets_path)),
                    )
                    .to_slash()
                    .unwrap_or_else(|| "".into())
                    .as_str(),
            );
            v.to_string()
        })
        .unwrap_or_else(|| "".into())
}

impl AssetsModel {
    pub fn new_with_site_path(path: String) -> Self {
        Self {
            id: None,
            path,
            is_directory: false,
            is_foreign: false,
            mime: None,
            filename: None,
            filename_ext: None,
            owner_id: None,
        }
    }
}

impl From<AssetsModel> for Assets {
    fn from(assets: AssetsModel) -> Self {
        Self {
            id: assets.id.unwrap_or_default(),
            path: relative_assets_path_to_web_path(&assets.path),
            is_directory: assets.is_directory,
            is_foreign: assets.is_foreign,
        }
    }
}

impl From<AssetsModel> for AssetsModelDTO {
    fn from(model: AssetsModel) -> Self {
        Self {
            id: model.id.map(|v| v.to_string()),
            path: relative_assets_path_to_web_path(&model.path),
            is_directory: model.is_directory,
            is_foreign: model.is_foreign,
            mime: model.mime,
            filename: model.filename,
            filename_ext: model.filename_ext,
        }
    }
}

impl Assets {
    pub fn new_with_site_path(path: String) -> Self {
        Self {
            id: ObjectId::new(),
            path,
            is_directory: false,
            is_foreign: false,
        }
    }
}
impl_into_bson!(Assets);
